package com.fastwire.carriere.hibernate.daoimpl;

import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.proxy.HibernateProxyHelper;
import org.hibernate.type.Type;

import com.fastwire.carriere.common.util.CommonLogger;
import com.fastwire.carriere.dao.DAOException;
import com.fastwire.carriere.inventory.entity.NetworkEntity;

// TODO: Auto-generated Javadoc
/**
 * The Class HibernateTemplate.
 */
public class HibernateTemplate {

	/**
	 * Find objs by criteria.
	 * 
	 * @param criteria
	 *            the criteria
	 * @param isCacheable
	 *            the is cacheable
	 * @return the list
	 * @throws DAOException
	 *             the dAO exception
	 */
	public List findObjsByCriteria(DetachedCriteria criteria,
			boolean isCacheable, boolean isForceLoad) throws DAOException {
		Session session = HibernateUtil.openSession();
		CommonLogger.info(this, "Opened a DB connection!");
		try {

			Criteria executableCriteria = criteria
					.getExecutableCriteria(session);

			if (isCacheable) {
				executableCriteria.setCacheable(true);
			}
			List results = executableCriteria.list();

			if (isForceLoad) {
				for (Object obj : results) {
					forceLoad(obj);
				}
			}
			return results;
		} catch (Exception ex) {
			if (session.getTransaction().isActive()) {
				session.getTransaction().rollback();
			}
			throw new DAOException(ex);
		} finally {
			HibernateUtil.close(session);

		}
	}

	/*
	 * overlaoded with passing session
	 */
	public List findObjsByCriteria(DetachedCriteria criteria,
			boolean isCacheable, boolean isForceLoad, Session session) throws DAOException {
		//Session session = HibernateUtil.openSession();
		try {

			Criteria executableCriteria = criteria
					.getExecutableCriteria(session);

			if (isCacheable) {
				executableCriteria.setCacheable(true);
			}
			List results = executableCriteria.list();

			if (isForceLoad) {
				for (Object obj : results) {
					forceLoad(obj);
				}
			}
			return results;
		} catch (Exception ex) {
			if (session.getTransaction().isActive()) {
				session.getTransaction().rollback();
			}
			throw new DAOException(ex);
		} 
		/*finally {
			HibernateUtil.close(session);

		}*/
	}
	/**
	 * Find single obj by criteria.
	 * 
	 * @param criteria
	 *            the criteria
	 * @param isCacheable
	 *            the is cacheable
	 * @return the object
	 * @throws DAOException
	 *             the dAO exception
	 */
	public Object findSingleObjByCriteria(DetachedCriteria criteria,
			boolean isCacheable, boolean isForceLoad) throws DAOException {
		Session session = HibernateUtil.openSession();
		try {

			Criteria executableCriteria = criteria
					.getExecutableCriteria(session);
			if (isCacheable) {
				executableCriteria.setCacheable(true);
			}

			Object object = executableCriteria.uniqueResult();

			if (isForceLoad) {
				forceLoad(object);

			}
			return object;
		} catch (Exception ex) {
			if (session.getTransaction().isActive()) {
				session.getTransaction().rollback();
			}
			throw new DAOException(ex);
		} finally {
			HibernateUtil.close(session);

		}
	}

	/**
	 * Save or update.
	 * 
	 * @param obj
	 *            the obj
	 * @throws DAOException
	 *             the dAO exception
	 */
	public void saveOrUpdate(Object obj) throws DAOException {
		Session session = HibernateUtil.openSession();
		try {

			session.beginTransaction();
			session.saveOrUpdate(obj);
			session.getTransaction().commit();
		} catch (Exception ex) {
			if (session.getTransaction().isActive()) {
				session.getTransaction().rollback();
			}
			throw new DAOException(ex);
		} finally {
			HibernateUtil.close(session);

		}
	}

	public void invalidateObjsByCriteria(DetachedCriteria criteria)
			throws DAOException {
		Session session = HibernateUtil.openSession();
		try {
			session.beginTransaction();
			Criteria executableCriteria = criteria
					.getExecutableCriteria(session);
			executableCriteria.setFetchSize(1000);

			ScrollableResults scrollableResults = executableCriteria
					.scroll(ScrollMode.FORWARD_ONLY);
			int count = 0;
			while (scrollableResults.next()) {
				//System.out.println("count is:" + count);
				Object object = scrollableResults.get(0);

				if (object instanceof NetworkEntity) {
					((NetworkEntity) object).invalidate();
				}

				if (++count % 20 == 0) {
					session.flush();
					session.clear();
				}

			}

			session.flush();
			session.clear();
			session.getTransaction().commit();

		} catch (Exception ex) {
			if (session.getTransaction().isActive()) {
				session.getTransaction().rollback();
			}
			throw new DAOException(ex);
		} finally {
			HibernateUtil.close(session);

		}
	}

	protected void forceLoad(Object entity) throws HibernateException {
		if (entity == null) {
			return;
		}
		Hibernate.initialize(entity);
		Class objlass = HibernateProxyHelper
				.getClassWithoutInitializingProxy(entity);

		ClassMetadata classMetadata = HibernateUtil.getSessionFactory()
				.getClassMetadata(objlass);

		if (classMetadata == null) {
			//CommonLogger.debug(this, "Entity : " + entity.getClass().getName() + " is not Hibernate Entity, so returning");
			return;
		}

		for (int i = 0, n = classMetadata.getPropertyNames().length; i < n; i++) {
			String propertyName = classMetadata.getPropertyNames()[i];			
			Type type = classMetadata.getPropertyType(propertyName);			
			if (type.isEntityType()) {
				Object subEntity = classMetadata.getPropertyValue(entity,
						propertyName, EntityMode.POJO);
				forceLoad(subEntity);

			}
			if (type.isCollectionType()) {
				Collection collection = (Collection) classMetadata
						.getPropertyValue(entity, propertyName, EntityMode.POJO);
				if (collection != null && collection.size() > 0) {
					for (Object collectionItem : collection) {
						forceLoad(collectionItem);
					}
				}
			}
		}
	}

}
